-- Jednostka arytmetyczno-logiczna
-- Wejscia danych:
-- X(7..0)
-- Y(7..0)
-- Wyjscia danych:
-- ALUOUT(7..0) (wynik operacji arytmetycznej) 
-- Wejscia sterujace:
-- INSTR(1..0) (rodzaj instrukcji)
-- OE# (output enable)
-- START# (start obliczen)
-- Wyjscia sterujace:
-- FLAGOUT(3..0) (wyjscie do rejestru flag)
-- FLAGW (ustawianie flag)

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_Arith.all;
use work.constants.all;

entity ALU3 is
  generic (delay : time := 5 ns);
  port (X, Y      : in std_logic_vector(7 downto 0);
        INSTR     : in std_logic_vector(1 downto 0);
        OE, START : in std_logic;
        ALUOUT    : out std_logic_vector(7 downto 0);
        FLAGOUT   : out std_logic_vector(3 downto 0);
        FLAGW     : out std_logic);
end entity ALU3;

architecture ALU3_arch of ALU3 is
  signal ALU2_din, ALU2_dout : std_logic_vector(7 downto 0);
	signal ALU2_flags : std_logic_vector(3 downto 0);
begin
  process(OE, START)
    variable buf : std_logic_vector(7 downto 0) := "00000000";
    variable dopelnienie, wynik : std_logic_vector(7 downto 0);
    variable carry : std_logic;
  begin
    if (START = '0' and INSTR = CMP) then
      FLAGW <= '0' after delay; -- umozliwienie modyfikacji rejestru flag
      -- obliczanie dopelnienia drugiego argumentu
      dopelnienie(0) := not Y(0) xor '1'; carry := not Y(0) and '1';
      dopelnienie(1) := not Y(1) xor carry; carry := not Y(1) and carry;
      dopelnienie(2) := not Y(2) xor carry; carry := not Y(2) and carry;
      dopelnienie(3) := not Y(3) xor carry; carry := not Y(3) and carry;
      dopelnienie(4) := not Y(4) xor carry; carry := not Y(4) and carry;
      dopelnienie(5) := not Y(5) xor carry; carry := not Y(5) and carry;
      dopelnienie(6) := not Y(6) xor carry; carry := not Y(6) and carry;
      dopelnienie(7) := not Y(7) xor carry;
      
      -- sumowanie dopelnienia i pierwszego argumentu
      wynik(0) := dopelnienie(0) xor X(0); carry := dopelnienie(0) and X(0);
      wynik(1) := dopelnienie(1) xor X(1) xor carry; carry := (dopelnienie(1) and X(1)) or (carry and (X(1) xor dopelnienie(1)));
      wynik(2) := dopelnienie(2) xor X(2) xor carry; carry := (dopelnienie(2) and X(2)) or (carry and (X(2) xor dopelnienie(2)));
      wynik(3) := dopelnienie(3) xor X(3) xor carry; carry := (dopelnienie(3) and X(3)) or (carry and (X(3) xor dopelnienie(3)));
      wynik(4) := dopelnienie(4) xor X(4) xor carry; carry := (dopelnienie(4) and X(4)) or (carry and (X(4) xor dopelnienie(4)));
      wynik(5) := dopelnienie(5) xor X(5) xor carry; carry := (dopelnienie(5) and X(5)) or (carry and (X(5) xor dopelnienie(5)));
      wynik(6) := dopelnienie(6) xor X(6) xor carry; carry := (dopelnienie(6) and X(6)) or (carry and (X(6) xor dopelnienie(6)));
      wynik(7) := dopelnienie(7) xor X(7) xor carry;
      
      -- ustawianie flag
      FLAGOUT(CF) <= (dopelnienie(7) and X(7)) or (carry and (X(7) xor dopelnienie(7))) after delay;
      FLAGOUT(ZF) <= not (wynik(0) or wynik(1) or wynik(2) or wynik(3) or wynik(4) or wynik(5) or wynik(6) or wynik(7)) after delay;
      FLAGOUT(SF) <= wynik(7) after delay; 
      FLAGOUT(OVF) <= wynik(7) xor (dopelnienie(7) and X(7)) after delay;
    elsif (START = '0' and INSTR = NEG) then
      FLAGW <= '0' after delay;
      
      ALU2_din <= X;
    end if;
    
    if(START = '1') then FLAGW <= '1' after delay; end if;
      
    if (OE = '0') then 
      if(INSTR = CMP) then ALUOUT <= buf;
      elsif (INSTR = NEG) then
        ALUOUT <= ALU2_dout;
        FLAGOUT <= ALU2_flags;
      end if;
    else
      ALUOUT <= "ZZZZZZZZ";
    end if;
  end process;
  
  ALU2: entity work.neg_instr
    port map (
      din     => ALU2_din,
		  dout    => ALU2_dout,
		  flagi   => ALU2_flags
    );

end architecture ALU3_arch;
